package com.wquick.base.compose.net

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import io.modifier.basic.compose.hooks.comm.Reducer
import io.modifier.basic.compose.hooks.comm.Tuple2
import io.modifier.basic.compose.hooks.comm.tuple
import io.modifier.basic.compose.hooks.getValue
import io.modifier.basic.compose.hooks.setValue
import io.modifier.basic.compose.hooks.useMount
import io.modifier.basic.compose.hooks.useRef
import io.modifier.basic.compose.hooks.useredux.createStore
import io.modifier.basic.compose.hooks.useredux.useDispatchAsync
import io.modifier.basic.compose.hooks.useredux.useSelector
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlin.math.pow
import kotlin.time.Duration.Companion.seconds

// 请求结果状态封装，同时这也是 Action
sealed interface NetFetchResult<out T> {
    data class Success<T>(
        val data: T,
    ) : NetFetchResult<T>

    data class Error(
        val msg: Throwable,
    ) : NetFetchResult<Nothing>

    data object Idle : NetFetchResult<Nothing>

    data object Loading : NetFetchResult<Nothing>
}

// 定义函数一个高级函数ReduxFetch，它接收一个挂起函数作为参数
typealias ReduxFetch<T> = (block: suspend CoroutineScope.() -> T) -> Unit

/**
 * 封住处理请求前loading、请求错误
 *
 * @param alias
 * @param T
 * @return
 */
@Composable
fun <T> useFetch(alias: String): ReduxFetch<T> {
    // 在函数调用时首先 dispatch Loading 状态
    val dispatchAsync =
        useDispatchAsync<NetFetchResult<T>>(alias, onBefore = { it(NetFetchResult.Loading) })
    return { block ->
        dispatchAsync {
            try {
                // 这里的block 就是上面定义的ReduxFetch的参数，是一个retrofit挂起函数
                NetFetchResult.Success(block())
            } catch (t: Throwable) {
                NetFetchResult.Error(t)
            }
        }
    }
}

// reducer
val fetchReducer: Reducer<NetFetchResult<*>, NetFetchResult<*>> = { _, action ->
    action
}

@Composable
fun <T> useFetchAliasFetch(
    alias: String,
    autoFetch: Boolean = false,
    errorRetry: Int = 0,
    block: suspend CoroutineScope.() -> T,
): Tuple2<NetFetchResult<T>, () -> Unit> {
    val fetchResult: NetFetchResult<T> by useSelector(alias)
    val dispatchFetch = useFetch<T>(alias)
    var retryCount by useRef(errorRetry)
    val fetch = {
        val count = errorRetry - retryCount
        dispatchFetch {
            delay((1.seconds * 2f.pow(count).toInt()).coerceAtMost(30.seconds))
            block()
        }
    }
    // 挂载时自动请求
    useMount {
        if (autoFetch && fetchResult is NetFetchResult.Idle) fetch()
    }
    // 错误重试
    when (fetchResult) {
        is NetFetchResult.Error -> {
            if (retryCount > 0) {
                fetch()
                retryCount -= 1
            }
        }
        is NetFetchResult.Success -> {
            retryCount = errorRetry
        }

        else -> {}
    }
    return tuple(
        first = fetchResult,
        second = fetch
    )
}

//<editor-fold desc="demo">

// 这里应该起名更有实际意义
private const val FetchAlias1 = "fetch1"
private const val FetchAlias2 = "fetch2"

// 用于创建在 createStore 中批量创建
private val NetworkFetchAliases = arrayOf(
    FetchAlias1,
    FetchAlias2,
)

val fetchStore = createStore {
    NetworkFetchAliases.forEach {
        named(it) {
            fetchReducer with NetFetchResult.Idle
        }
    }
}

// 使用 `useFetchAliasFetch` 轻松的为每一个请求派生出自定义hook
@Composable
private fun useFetchError() = useFetchAliasFetch<String>(alias = FetchAlias1) {
    delay(2.seconds)
    error("fetch error") // 手动抛出异常
}

@Composable
private fun useFetchUserInfo(user: String = "123") = useFetchAliasFetch<String>(alias = FetchAlias2, autoFetch = true) {
    // 用来进行网络请求的 suspend 函数
    "123"
}

//</editor-fold>



